package com.imooc.shell.service;

import com.imooc.shell.enums.ResultEnum;
import com.imooc.shell.exception.SellException;
import lombok.extern.slf4j.Slf4j;
import org.simpleframework.xml.core.Commit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;

@Commit
@Slf4j
public class RedisLock {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    private static final int TIMEOUT = 10; // 超时时间 「秒」


    /**
     * 示例方法
     */
    public void testLock() {

        long time = System.currentTimeMillis() + TIMEOUT;

        // 加锁
        if (!lock("productId", String.valueOf(time))) {
            throw new SellException(101, "哎哟喟，人也太多了，换个姿势再试试~~");
        }
        // 业务逻辑

        // 解锁
        unlock("productId", String.valueOf(time));
    }

    /**
     * 加锁
     *
     * @param key
     * @param value 当前时间+超时时间
     * @return
     */
    public boolean lock(String key, String value) {
        if (stringRedisTemplate.opsForValue().setIfAbsent(key, value)) { // setnx 命令
            return true; //已经锁住了
        }
        // 如果不写下面的代码可能会造成死锁情况: setnx成功了，但是解锁不成功会导致死循环

        // currentValue = A 这两个线程的 value 都是 B 让其中一个线程拿到锁
        String currentValue = stringRedisTemplate.opsForValue().get(key);
        // 如果锁过期
        if (!StringUtils.isEmpty(currentValue)
                && Long.parseLong(currentValue) < System.currentTimeMillis()) {
            // 获取上一个锁的时间
            String oldValue = stringRedisTemplate.opsForValue().getAndSet(key, value);
            if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
                return true;
            }

        }

        return false;
    }

    /**
     * 解锁
     *
     * @param key
     * @param value
     */
    public void unlock(String key, String value) {

        try {

            String currentValue = stringRedisTemplate.opsForValue().get(key);
            if (!StringUtils.isEmpty(currentValue)
                    && currentValue.equals(value)) {
                stringRedisTemplate.opsForValue().getOperations().delete(key);

            }
        } catch (Exception e) {
            log.error("「redis 分布式锁」 解锁异常");
        }

    }
}
